<?php
/**
 * @version		$Id$
 * @copyright	Copyright (C) Fiji Web Design
 * @license		GNU/GPL, see LICENSE.php
 */

// Check to ensure this file is included in Joomla!
defined( '_JEXEC' ) or die( 'Restricted access' );

jimport( 'joomla.plugin.plugin' );

/**
 * Example Content Plugin
 *
 * @package		Joomla
 * @subpackage	Content
 * @since 		1.5
 */
class plgContentGeshibot extends JPlugin
{
	
	static $loadid;

	/**
	 * Constructor
	 *
	 * For php4 compatability we must not use the __constructor as a constructor for plugins
	 * because func_get_args ( void ) returns a copy of all passed arguments NOT references.
	 * This causes problems with cross-referencing necessary for the observer design pattern.
	 *
	 * @param object $subject The object to observe
	 * @param object $params  The object that holds the plugin parameters
	 * @since 1.5
	 */
	function plgContentGeshibot( &$subject, $params )
	{
		parent::__construct( $subject, $params );
		
		self::$loadid = 0;
	}

	/**
	 * Example prepare content method
	 *
	 * Method is called by the view
	 *
	 * @param 	object		The article object.  Note $article->text is also available
	 * @param 	object		The article params
	 * @param 	int			The 'page' number
	 */
	function onPrepareContent( &$article, &$params, $limitstart )
	{
		$Application =& JFactory::getApplication();
		$Database =& JFactory::getDBO();
		$Document =& JFactory::getDocument();
		
		// retrieve tag used
		if (intval($this->params->def( 'tag' )) == 0) {
			$regex = "/<pre\s*(.*?)>(.*?)<\/pre>/is";
		} else {
			$regex = "/{geshibot(\s|&nbsp;)*(.*?)}(.*?){\/geshibot}/is";
		}
		
		// define users gid for access priviledges
		$authorid = $article->modified_by ? $article->modified_by : $article->created_by;
		$query = "SELECT gid from #__users WHERE id = ".intval($authorid)." LIMIT 1";
		$Database->setQuery($query);
		$gid = $Database->loadResult();
		
		// replace code with syntax highlighted code
		$article->text = str_replace('{joomla_dir}', JURI::base(), $article->text);
		$article->text = preg_replace_callback($regex, array($this, 'replace_init'), $article->text);
		
		// add external JS
		JHTML::_( 'behavior.mootools' );
		$Document->addScript('plugins/content/geshibot/js/geshibot.js');
		
		// buffer inline JS
		ob_start();
		?>
		
		if (typeof(Geshibot) == 'undefined') {
			Geshibot = {};
		}
		
		Geshibot.copy_type = '<?php echo intval($this->params->def( "show_copy_type")); ?>';
		Geshibot.show_copy_label = '<?php echo $this->params->def( "show_copy_label", "View Source" ); ?>';
	
		<?php 
		if ($this->params->def( 'joomla_css' ) == 'yes' && !defined('geshi_joomla_css')) { 
		define('geshi_joomla_css', 1);
		?>
		// initiate our class replacements
		window.addEvent('domready', function() {
			Geshibot.replaceGeshiWithMamboClasses();
		});
		<?php } ?>
		
		<?php 
		if ($this->params->def( 'show_copy' ) == 'yes' && !defined('geshi_show_copy')) { 
		define('geshi_show_copy', 1);
		?>
	
		// initiate our class replacements
		window.addEvent('domready', function() {
			Geshibot.addCodeView();
		});
		
		<?php } ?>
		<?php
		$js = ob_get_clean();
		
		// add JS to head
		$Document->addScriptDeclaration($js);
		
		return true;

	}
	
	// get the right match before sending it to our replace function
	function replace_init($matches) {
		
		if (intval($this->params->def( 'tag' )) == 0) {
			return $this->replace($matches);
		} else {
			$_matches = array($matches[0], str_replace('&quot;', '"', $matches[2]), $matches[3]);
			return $this->replace($_matches);
		}
		
		
	}
	
	// callback function for preg_replace_callback
	function replace($matches) {
		
		// language
		preg_match("/(language|lang)=\"([^\"]*)\"/", $matches[1], $_matches);
		$language = isset($_matches[2]) ? $_matches[2] : ''; 
		
		// file
		preg_match("/(filepath|file)=\"([^\"]*)\"/", $matches[1], $_matches);
		$file = isset($_matches[2]) ? $_matches[2] : false; 
		
		// source, htmlDecoded
		if ($file) {
			$source = $this->file_get_contents($file);
		} else {
			$source = $this->decodeHtmlEntities($matches[2]);
		}
		
		// header
		preg_match("/(header|head)=\"([^\"]*)\"/", $matches[1], $_matches);
		$header = isset($_matches[2]) ? $_matches[2] : '';
		
		// footer
		preg_match("/(footer|foot)=\"([^\"]*)\"/", $matches[1], $_matches);
		$footer = isset($_matches[2]) ? $_matches[2] : ' &nbsp; ';
		
		//echo geshibot_object2HTML(array('tag_attribs'=>$matches[1], 'language'=>$language, 'header'=>$header, 'footer'=>$footer, 'file'=>$file,'source'=>$source)); // debug
	 
		// highlight code
		$h_source = $this->geshi ($source, $language, $header, $footer);
		
		if ($this->params->def( 'show_copy' ) == 'yes') {
			// return the highlighted code and original code in a div with unique id
			self::$loadid++;
			$o_source = '<div class="original_source_'.self::$loadid.'" style="display:none;">'.$this->encode($source).'</div>';
			return '<div id="geshibot_load_'.self::$loadid.'_'.sha1($source).'">'.$h_source.$o_source.'</div>';
		} else {
			// return just the highlighted source
			return $h_source;
		}
	}
	
	function geshi ($source, $language = 'php', $header = 'Code:', $footer = ' ') {

		// include the geshi class
		if (!@require_once(JPATH_BASE.'/plugins/content/geshibot/geshi.class.php')) {
			return false;
		}
		
		// First the initialisation: source code to highlight and the language to use. Make sure
	    // you sanitise correctly if you use $_POST of course - this very script has had a security
	    // advisory against it in the past because of this. Please try not to use this script on a
	    // live site.
		$geshi =& new GeSHi($source, $language);
		
		// Load the Geshi CSS once for each language, if set in Parameters
		if ($this->params->def( 'geshibot_css' ) == 'yes') {
			if ( !defined($language.'_css') ) {
				echo '<style type="text/css">';
				echo $geshi->get_stylesheet();
				echo '</style>';
				define($language.'_css', true);
			}
		}
	    
	    // Use the PRE header. This means less output source since we don't have to output &nbsp;
	    // everywhere. Of course it also means you can't set the tab width.
		$geshi->set_header_type(GESHI_HEADER_PRE);
	    
	    // Enable CSS classes. You can use get_stylesheet() to output a stylesheet for your code. Using
	    // CSS classes results in much less output source.
		$geshi->enable_classes();
	    
	    // Enable line numbers. We want fancy line numbers, and we want every 5th line number to be fancy
		if ($this->params->def( 'lines' ) == 'yes') {
			$geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, $this->params->def( 'fancylinenum' ));
		}
	    
	    // Set the style for the PRE around the code. The line numbers are contained within this box (not
	    // XHTML compliant btw, but if you are liberally minded about these things then you'll appreciate
	    // the reduced source output).
		//$geshi->set_overall_style('color: #000066; border: 1px solid #d0d0d0; background-color: #f0f0f0;', true);
	    
		// Set the style for line numbers. In order to get style for line numbers working, the <li> element
	    // is being styled. This means that the code on the line will also be styled, and most of the time
	    // you don't want this. So the set_code_style reverts styles for the line (by using a <div> on the line).
	    // So the source output looks like this:
	    //
	    // <pre style="[set_overall_style styles]"><ol>
	    // <li style="[set_line_style styles]"><div style="[set_code_style styles]>...</div></li>
	    // ...
	    // </ol></pre>
		$geshi->set_line_style('font: normal normal 95% \'Courier New\', Courier, monospace; color: #003030;', 'font-weight: bold; color: #006060;', true);
		$geshi->set_code_style('color: #000020;', 'color: #000020;');
	
	    // Styles for hyperlinks in the code. GESHI_LINK for default styles, GESHI_HOVER for hover style etc...
	    // note that classes must be enabled for this to work.
		$geshi->set_link_styles(GESHI_LINK, 'color: #000060;');
		$geshi->set_link_styles(GESHI_HOVER, 'background-color: #f0f000;');
	
	    // Use the header/footer functionality. This puts a div with content within the PRE element, so it is
	    // affected by the styles set by set_overall_style. So if the PRE has a border then the header/footer will
	    // appear inside it.
		$geshi->set_header_content($header);
		//$geshi->set_header_content_style('font-family: Verdana, Arial, sans-serif; color: #808080; font-size: 70%; font-weight: bold; background-color: #f0f0ff; border-bottom: 1px solid #d0d0d0; padding: 2px;');
	
	    // You can use <TIME> and <VERSION> as placeholders
		$geshi->set_footer_content($footer);
		//$geshi->set_footer_content_style('font-family: Verdana, Arial, sans-serif; color: #808080; font-size: 70%; font-weight: bold; background-color: #f0f0ff; border-top: 1px solid #d0d0d0; padding: 2px;');
		
		// highlight the code using geshi
		return $geshi->parse_code();
	}
	
	// decodes html entities. Do we need this crap?
	function decodeHtmlEntities($text) {
	
		$html_entities_match = array( "|\<BR>|","|\<br \/\>|", "#<#", "#>#", "|&#39;|", '#&quot;#', '#&nbsp;#' );
		$html_entities_replace = array( "\n","\n", '&lt;', '&gt;', "'", '"', ' ' );
	
		$text = preg_replace( $html_entities_match, $html_entities_replace, $text );
	
		$text = str_replace('&lt;', '<', $text);
		$text = str_replace('&gt;', '>', $text);
	
	/*
		// Replace 2 spaces with "&nbsp; " so non-tabbed code indents without making huge long lines.
		$text = str_replace("  ", "&nbsp; ", $text);
		// now Replace 2 spaces with " &nbsp;" to catch odd #s of spaces.
		$text = str_replace("  ", " &nbsp;", $text);
	*/
		// Replace tabs with "&nbsp; &nbsp;" so tabbed code indents sorta right without making huge long lines.
		$text = str_replace("\t", "&nbsp; &nbsp;", $text);
		//$text = str_replace( "\t", '  ', $text );
		return $text;
	}
	
	// gets the content of a file if the article author is allowed access
	function file_get_contents($file, $gid = 0) {
		
		if ($gid < intval($this->params->def( 'acl_files' ))) {
			// this user does not have enough access to get file contents
			$content = $this->params->def( 'acl_files_msg' ).' author gid:'.$gid.' allowed gid:'.intval($this->params->def( 'acl_files' ));
		} else {
			$content = file_get_contents($file);
		}
	
		return $content;
	}
	
	function encode($text) {
		return rawurlencode($text);
	}


}

